#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
using namespace std;
enum ExitCode
{
    USAGE_ERR = 1,
    SOCKET_ERR,
};
enum class Status // C++11 强类型枚举，会做类型检查
{
    NEW,          // 新的连接
    CONNECTING,   // 正在连接
    CONNECTED,    // 已经连接
    DISCONNECTED, // 失联
    CLOSED        // 重连失败
};
static const int defaultsockfd = -1;
static const int defaultretry_interval = 1;
static const int defaultmax_retry_count = 5;

class ClientConnection
{
public:
    ClientConnection(const string &serverip, uint16_t serverport)
        : _sockfd(defaultsockfd),
          _serverip(serverip),
          _serverport(serverport),
          _status(Status::NEW),
          _retry_interval(defaultretry_interval),
          _max_retry_count(defaultmax_retry_count)
    {
    }
    Status ConnetcionStatus()
    {
        return _status;
    }
    void Connect()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd == -1)
        {
            cout << "create socket failed" << endl;
            exit(ExitCode::SOCKET_ERR);
        }
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr); // 把字符串风格的地址，转化成四字节的IP地址，放在server.sin_addr
        memset(&server, 0, sizeof(server));
        int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
        if (n == -1) // 连接失败
        {
            // 1, 关闭sockfd
            Disconnect();
            // 2, 更新状态
            _status = Status::DISCONNECTED; // 我们期望要重新连接，所以状态不能设置成CLOSED，CLOSED表示重连失败
            // 3，返回
            return;
        }
        _status = Status::CONNECTED; // 连接成功，状态设置为CONNECTED
    }
    void Process() // 正常的IO通信
    {
        while (true)
        {
            string msg = "hello server";
            ssize_t n = send(_sockfd, msg.c_str(), msg.size(), 0);
            if (n > 0) // 发送成功
            {
                char buffer[1024];
                ssize_t m = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
                if (m > 0)
                {
                    buffer[m] = 0;
                    std::cout << "server echo# " << buffer << std::endl;
                }
                else
                {
                    _status = Status::DISCONNECTED;
                    break;
                }
            }
            else // 发送失败
            {
                std::cerr << "send failed" << std::endl;
                _status = Status::CLOSED;
                break;
            }
        }
    }
    void Reconnect() // 在这里才将状态设置为 CLOSED
    {
        _status = Status::CONNECTING; // 状态设置为CONNECTING,表示正在重连,其实设不设都无所谓
        int cnt = 0;
        while (cnt < _max_retry_count)
        {
            Connect();                        // 就是不断调用Connect
            if (_status == Status::CONNECTED) // Connect成功之后，会修改_status的状态，重连成功,直接退出
            {
                break;
            }
            cnt++;
            if (cnt > _max_retry_count) // 重试次数达到上限
            {
                _status = Status::CLOSED; // 重连失败，状态设置为CLOSED
                cout << "重连失败，请检查你的网络..." << endl;
                break;
            }
            std::cout << "断线重连中，重连次数：" << cnt << std::endl;
            sleep(_retry_interval); // 重试间隔
        }
    }
    void Disconnect()
    {
        if (_sockfd != defaultsockfd) // 表示文件描述符正在使用，需要关闭
        {
            close(_sockfd);
            _sockfd = defaultsockfd; // 重置
        }
    }
    ~ClientConnection()
    {
    }

private:
    int _sockfd;
    string _serverip;
    uint16_t _serverport;
    Status _status;       // 连接的状态
    int _retry_interval;  // 重连间隔
    int _max_retry_count; // 最大重试次数
};

class TcpClient
{
public:
    TcpClient(const string &serverip, uint16_t serverport) : _connection(serverip, serverport)
    {
    }
    void Execute()
    {
        while (true)
        {
            switch (_connection.ConnetcionStatus()) // 循环判断连接状态
            {
            case Status::NEW:
                _connection.Connect(); // 发起连接
                break;
            case Status::CONNECTED:
                _connection.Process(); // 正常IO通信
                break;
            case Status::DISCONNECTED:
                _connection.Reconnect(); // 重连
                break;
            case Status::CLOSED:
                _connection.Disconnect(); // 关闭连接
                return;                   // 退出循环
            default:
                break;
            }
        }
    }
    ~TcpClient()
    {
    }

private:
    ClientConnection _connection;
};

int main(int argc, char *agrv[])
{
    if (argc != 3)
    {
        cout << "Usage: TcpClient <ip> <port>" << endl;
        exit(ExitCode::USAGE_ERR);
    }
    std::string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);
    TcpClient client(serverip, serverport);
    client.Execute();
    return 0;
}